/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "filesystem.h"
#include "node/fs/file.h"
#include <cstring>
#include <boost/bind.hpp>
using File = ::node::fs::File;
using ThreadPool = ::node::sys::ThreadPool;

namespace node {
    namespace fs {

        FileSystem *FileSystem::instance_ = NULL;

        FileSystem::FileSystem(void)
            : state_(UNINITIALIZED)
            , background_task_pool_()
        {
            assert(instance_ == NULL);
            instance_ = this;
            memset(&config_, 0, 
                sizeof(struct ::node::fs::FileSystem::Config));
        }

        FileSystem::~FileSystem(void)
        {
            Uninitialize();
        }

        bool FileSystem::Initialize(struct Config &config)
        {
            memcpy(&config_, &config, 
                sizeof(struct ::node::fs::FileSystem::Config));

            do 
            {
                // startup background task pool
                if (!background_task_pool_.Start(config_.background_task_worker_num)) {
                    break;
                }

                state_ = INITIALIZED;
                return true;
            } while (false);

            return false;
        }

        void FileSystem::Uninitialize(void)
        {
            if (state_ != INITIALIZED) {
                return;
            }

            // stop background task pool
            background_task_pool_.WaitAllTaskCompletedAndStop();
            state_ = UNINITIALIZED;
        }

        bool FileSystem::OpenFileAsync(const char *filename, int openmode, int flags, FileOpenAsyncHandler callback)
        {
            do 
            {
                ThreadPool::Task *open_task = background_task_pool_.NewTask();
                if (!open_task) {
                    break;
                }

                File *open_file = new File;
                open_task->user_data = open_file;
                open_task->task = ::boost::bind(&File::Open, open_file, filename, openmode, flags);
                open_task->complete_callback = ::boost::bind(callback, (File*)open_task->user_data);

                if (background_task_pool_.SubmitTask(open_task)) {
                    return true;
                }
                
                background_task_pool_.FreeTask(open_task);
            } while (false);
            return false;
        }

        bool FileSystem::ReadFileAsync(::node::fs::File *file, char *buffer, int size, FileWriteAsyncHandler callback)
        {
            do
            {
                ThreadPool::Task *read_task = background_task_pool_.NewTask();
                if (!read_task) {
                    break;
                }

                read_task->task = ::boost::bind(&FileSystem::ReadFileFunction, this,
                    file, buffer, size, callback);

                if (background_task_pool_.SubmitTask(read_task)) {
                    return true;
                }

                background_task_pool_.FreeTask(read_task);
            } while (false);
            return false;
        }

        bool FileSystem::WriteFileAsync(::node::fs::File *file, const char *buffer, int size, FileWriteAsyncHandler callback)
        {
            do
            {
                ThreadPool::Task *write_task = background_task_pool_.NewTask();
                if (!write_task) {
                    break;
                }

                write_task->task = ::boost::bind(&FileSystem::WriteFileFunction, this, 
                    file, buffer, size, callback);

                if (background_task_pool_.SubmitTask(write_task)) {
                    return true;
                }

                background_task_pool_.FreeTask(write_task);
            } while (false);
            return false;
        }

        bool FileSystem::ReadFileAsync(const char *filename, char *buffer, int size, FileReadAsyncHandler callback)
        {
            do
            {
                ThreadPool::Task *read_task = background_task_pool_.NewTask();
                if (!read_task) {
                    break;
                }

                read_task->task = ::boost::bind(&FileSystem::ReadFileFunctionByName, this,
                    filename, buffer, size, callback);

                if (background_task_pool_.SubmitTask(read_task)) {
                    return true;
                }

                background_task_pool_.FreeTask(read_task);
            } while (false);
            return false;
        }

        ///////////////////////////////////////////////////////////////////////

        void FileSystem::WriteFileFunction(::node::fs::File *file, 
            const char *buffer, int size, FileWriteAsyncHandler callback)
        {
            callback(file, file->Write(buffer, size));
        }

        void FileSystem::ReadFileFunction(::node::fs::File *file, 
            char *buffer, int size, FileReadAsyncHandler callback)
        {
            callback(file, file->Read(buffer, size));
        }

        void FileSystem::ReadFileFunctionByName(const char *filename, 
            char *buffer, int size, FileReadAsyncHandler callback)
        {
            {
                File file;
                if (file.Open(filename, File::OM_READ | File::OM_EXCL))
                {
                    callback(NULL, file.Read(buffer, size));
                    return;
                }
            }
            callback(NULL, -1);
        }
    }
}